package com.d3works.apps.base.rest.http;

import com.d3works.apps.base.rest.SimpleTrustManager;
import com.d3works.apps.base.rest.common.S;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.HttpsURLConnection;
import org.apache.commons.lang3.StringUtils;

public class HttpClient {
  public static final Pattern AuthMatcher = Pattern.compile("://([^\\:]+\\:[^@]+)@");

  public static final String GET = "GET";

  public static final String POST = "POST";

  public static final String PUT = "PUT";

  public static final String DELETE = "DELETE";

  public static class DefaultDelegateRequestEvent implements RequestEvent<Object> {
    private HttpClientRequest beforeRequest;

    private HttpClientResponse afterResponse;

    public DefaultDelegateRequestEvent(
        HttpClientRequest beforeRequest, HttpClientResponse afterResponse) {
      this.beforeRequest = beforeRequest;
      this.afterResponse = afterResponse;
    }

    @Override
    public void onRequest(Object body, RequestEventArgs request) throws Exception {
      if (this.beforeRequest != null) {
        this.beforeRequest.call(body, request);
      }
    }

    @Override
    public Object onResponsed(ResponseEventArgs response) throws Exception {
      if (this.afterResponse != null) {
        return this.afterResponse.call(response);
      } else {
        return response.getResponseText();
      }
    }
  }

  public static <T> T get(String url, RequestEvent<T> request) throws Exception {
    HttpURLConnection conn = getConn(url);
    conn.setRequestMethod("GET");
    conn.setDoOutput(true);
    RequestEventArgs args = new RequestEventArgs(conn, "GET");
    request.onRequest(null, args);
    conn.connect();
    return request.onResponsed(new ResponseEventArgs(conn));
  }

  public static <T> T post(String url, Object data, RequestEvent<T> request) throws Exception {
    HttpURLConnection conn = getConn(url);
    conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
    conn.setRequestMethod("POST");
    conn.setDoOutput(true);
    RequestEventArgs e = new RequestEventArgs(conn, "POST");
    e.jsonPost();
    request.onRequest(data, e);
    conn.connect();
    return request.onResponsed(new ResponseEventArgs(conn));
  }

  /**
   * 创建HTTP请求连接
   *
   * @param url
   * @return
   * @throws IOException
   */
  private static HttpURLConnection getConn(String url) throws IOException {
    Matcher m = AuthMatcher.matcher(url);
    StringBuffer sb = new StringBuffer();
    String auth = null;
    String Authorization = null;
    URL realUrl = null;
    if (m.find()) {
      m.appendReplacement(sb, "://");
      auth = m.group(1);
      byte[] base64Encode = Base64.getEncoder().encode(auth.getBytes());
      Authorization = "Basic " + new String(base64Encode);
      m.appendTail(sb);
      realUrl = new URL(sb.toString());
    } else {
      realUrl = new URL(url);
    }

    HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
    conn.setRequestProperty("accept", "*/*");
    conn.setRequestProperty("Connection", "Keep-Alive");
    conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
    if (StringUtils.isNotEmpty(Authorization)) {
      conn.setRequestProperty("Authorization", Authorization);
    }
    conn.setDoOutput(true);
    conn.setDoInput(true);
    conn.setInstanceFollowRedirects(false);
    if (conn instanceof HttpsURLConnection) {
      try {
        SimpleTrustManager.setTrust((HttpsURLConnection) conn);
      } catch (Exception e) {
        throw new IOException(e.getMessage(), e);
      }
    }
    return conn;
  }

  /**
   * 创建URL参数字符串
   *
   * @param data
   * @return
   */
  public static String getRequestParam(Object data) {
    if (data == null) {
      return null;
    } else if (data instanceof Map<?, ?>) {
      List<String> requestParams = new ArrayList<>();
      for (Map.Entry<?, ?> entry : ((Map<?, ?>) data).entrySet()) {
        String name = entry.getKey().toString();
        Object oValue = entry.getValue();
        String value = oValue != null ? oValue.toString() : "";
        value = urlEncode(value);
        requestParams.add(String.format("%s=%s", name, value));
      }
      return S.join(requestParams, "&");
    } else {
      return data.toString();
    }
  }

  private static String urlEncode(String content) {
    try {
      return URLEncoder.encode(content, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }
}
